/**
 * vuex v2.0.0
 * (c) 2016 Evan You
 * @license MIT
 */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
        typeof define === 'function' && define.amd ? define(factory) :
            (global.Vuex = factory());
}(this, (function () { 'use strict';

    var devtoolHook =
        typeof window !== 'undefined' &&
        window.__VUE_DEVTOOLS_GLOBAL_HOOK__

    function devtoolPlugin (store) {
        if (!devtoolHook) { return }

        store._devtoolHook = devtoolHook

        devtoolHook.emit('vuex:init', store)

        devtoolHook.on('vuex:travel-to-state', function (targetState) {
            store.replaceState(targetState)
        })

        store.subscribe(function (mutation, state) {
            devtoolHook.emit('vuex:mutation', mutation, state)
        })
    }

    function applyMixin (Vue) {
        var version = Number(Vue.version.split('.')[0])

        if (version >= 2) {
            var usesInit = Vue.config._lifecycleHooks.indexOf('init') > -1
            Vue.mixin(usesInit ? { init: vuexInit } : { beforeCreate: vuexInit })
        } else {
            // override init and inject vuex init procedure
            // for 1.x backwards compatibility.
            var _init = Vue.prototype._init
            Vue.prototype._init = function (options) {
                if ( options === void 0 ) options = {};

                options.init = options.init
                    ? [vuexInit].concat(options.init)
                    : vuexInit
                _init.call(this, options)
            }
        }

        /**
         * Vuex init hook, injected into each instances init hooks list.
         */

        function vuexInit () {
            var options = this.$options
            // store injection
            if (options.store) {
                this.$store = options.store
            } else if (options.parent && options.parent.$store) {
                this.$store = options.parent.$store
            }
        }
    }

    function mapState (states) {
        var res = {}
        normalizeMap(states).forEach(function (ref) {
            var key = ref.key;
            var val = ref.val;

            res[key] = function mappedState () {
                return typeof val === 'function'
                    ? val.call(this, this.$store.state, this.$store.getters)
                    : this.$store.state[val]
            }
        })
        return res
    }

    function mapMutations (mutations) {
        var res = {}
        normalizeMap(mutations).forEach(function (ref) {
            var key = ref.key;
            var val = ref.val;

            res[key] = function mappedMutation () {
                var args = [], len = arguments.length;
                while ( len-- ) args[ len ] = arguments[ len ];

                return this.$store.commit.apply(this.$store, [val].concat(args))
            }
        })
        return res
    }

    function mapGetters (getters) {
        var res = {}
        normalizeMap(getters).forEach(function (ref) {
            var key = ref.key;
            var val = ref.val;

            res[key] = function mappedGetter () {
                if (!(val in this.$store.getters)) {
                    console.error(("[vuex] unknown getter: " + val))
                }
                return this.$store.getters[val]
            }
        })
        return res
    }

    function mapActions (actions) {
        var res = {}
        normalizeMap(actions).forEach(function (ref) {
            var key = ref.key;
            var val = ref.val;

            res[key] = function mappedAction () {
                var args = [], len = arguments.length;
                while ( len-- ) args[ len ] = arguments[ len ];

                return this.$store.dispatch.apply(this.$store, [val].concat(args))
            }
        })
        return res
    }

    function normalizeMap (map) {
        return Array.isArray(map)
            ? map.map(function (key) { return ({ key: key, val: key }); })
            : Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); })
    }

    function isObject (obj) {
        return obj !== null && typeof obj === 'object'
    }

    function isPromise (val) {
        return val && typeof val.then === 'function'
    }

    function assert (condition, msg) {
        if (!condition) { throw new Error(("[vuex] " + msg)) }
    }

    var Vue // bind on install

    var Store = function Store (options) {
        var this$1 = this;
        if ( options === void 0 ) options = {};

        assert(Vue, "must call Vue.use(Vuex) before creating a store instance.")
        assert(typeof Promise !== 'undefined', "vuex requires a Promise polyfill in this browser.")

        var state = options.state; if ( state === void 0 ) state = {};
        var plugins = options.plugins; if ( plugins === void 0 ) plugins = [];
        var strict = options.strict; if ( strict === void 0 ) strict = false;

        // store internal state
        this._options = options
        this._committing = false
        this._actions = Object.create(null)
        this._mutations = Object.create(null)
        this._wrappedGetters = Object.create(null)
        this._runtimeModules = Object.create(null)
        this._subscribers = []
        this._watcherVM = new Vue()

        // bind commit and dispatch to self
        var store = this
        var ref = this;
        var dispatch = ref.dispatch;
        var commit = ref.commit;
        this.dispatch = function boundDispatch (type, payload) {
            return dispatch.call(store, type, payload)
        }
        this.commit = function boundCommit (type, payload, options) {
            return commit.call(store, type, payload, options)
        }

        // strict mode
        this.strict = strict

        // init root module.
        // this also recursively registers all sub-modules
        // and collects all module getters inside this._wrappedGetters
        installModule(this, state, [], options)

        // initialize the store vm, which is responsible for the reactivity
        // (also registers _wrappedGetters as computed properties)
        resetStoreVM(this, state)

        // apply plugins
        plugins.concat(devtoolPlugin).forEach(function (plugin) { return plugin(this$1); })
    };

    var prototypeAccessors = { state: {} };

    prototypeAccessors.state.get = function () {
        return this._vm.state
    };

    prototypeAccessors.state.set = function (v) {
        assert(false, "Use store.replaceState() to explicit replace store state.")
    };

    Store.prototype.commit = function commit (type, payload, options) {
        var this$1 = this;

        // check object-style commit
        if (isObject(type) && type.type) {
            options = payload
            payload = type
            type = type.type
        }
        var mutation = { type: type, payload: payload }
        var entry = this._mutations[type]
        if (!entry) {
            console.error(("[vuex] unknown mutation type: " + type))
            return
        }
        this._withCommit(function () {
            entry.forEach(function commitIterator (handler) {
                handler(payload)
            })
        })
        if (!options || !options.silent) {
            this._subscribers.forEach(function (sub) { return sub(mutation, this$1.state); })
        }
    };

    Store.prototype.dispatch = function dispatch (type, payload) {
        // check object-style dispatch
        if (isObject(type) && type.type) {
            payload = type
            type = type.type
        }
        var entry = this._actions[type]
        if (!entry) {
            console.error(("[vuex] unknown action type: " + type))
            return
        }
        return entry.length > 1
            ? Promise.all(entry.map(function (handler) { return handler(payload); }))
            : entry[0](payload)
    };

    Store.prototype.subscribe = function subscribe (fn) {
        var subs = this._subscribers
        if (subs.indexOf(fn) < 0) {
            subs.push(fn)
        }
        return function () {
            var i = subs.indexOf(fn)
            if (i > -1) {
                subs.splice(i, 1)
            }
        }
    };

    Store.prototype.watch = function watch (getter, cb, options) {
        var this$1 = this;

        assert(typeof getter === 'function', "store.watch only accepts a function.")
        return this._watcherVM.$watch(function () { return getter(this$1.state); }, cb, options)
    };

    Store.prototype.replaceState = function replaceState (state) {
        var this$1 = this;

        this._withCommit(function () {
            this$1._vm.state = state
        })
    };

    Store.prototype.registerModule = function registerModule (path, module) {
        if (typeof path === 'string') { path = [path] }
        assert(Array.isArray(path), "module path must be a string or an Array.")
        this._runtimeModules[path.join('.')] = module
        installModule(this, this.state, path, module)
        // reset store to update getters...
        resetStoreVM(this, this.state)
    };

    Store.prototype.unregisterModule = function unregisterModule (path) {
        var this$1 = this;

        if (typeof path === 'string') { path = [path] }
        assert(Array.isArray(path), "module path must be a string or an Array.")
        delete this._runtimeModules[path.join('.')]
        this._withCommit(function () {
            var parentState = getNestedState(this$1.state, path.slice(0, -1))
            Vue.delete(parentState, path[path.length - 1])
        })
        resetStore(this)
    };

    Store.prototype.hotUpdate = function hotUpdate (newOptions) {
        updateModule(this._options, newOptions)
        resetStore(this)
    };

    Store.prototype._withCommit = function _withCommit (fn) {
        var committing = this._committing
        this._committing = true
        fn()
        this._committing = committing
    };

    Object.defineProperties( Store.prototype, prototypeAccessors );

    function updateModule (targetModule, newModule) {
        if (newModule.actions) {
            targetModule.actions = newModule.actions
        }
        if (newModule.mutations) {
            targetModule.mutations = newModule.mutations
        }
        if (newModule.getters) {
            targetModule.getters = newModule.getters
        }
        if (newModule.modules) {
            for (var key in newModule.modules) {
                if (!(targetModule.modules && targetModule.modules[key])) {
                    console.warn(
                        "[vuex] trying to add a new module '" + key + "' on hot reloading, " +
                        'manual reload is needed'
                    )
                    return
                }
                updateModule(targetModule.modules[key], newModule.modules[key])
            }
        }
    }

    function resetStore (store) {
        store._actions = Object.create(null)
        store._mutations = Object.create(null)
        store._wrappedGetters = Object.create(null)
        var state = store.state
        // init root module
        installModule(store, state, [], store._options, true)
        // init all runtime modules
        Object.keys(store._runtimeModules).forEach(function (key) {
            installModule(store, state, key.split('.'), store._runtimeModules[key], true)
        })
        // reset vm
        resetStoreVM(store, state)
    }

    function resetStoreVM (store, state) {
        var oldVm = store._vm

        // bind store public getters
        store.getters = {}
        var wrappedGetters = store._wrappedGetters
        var computed = {}
        Object.keys(wrappedGetters).forEach(function (key) {
            var fn = wrappedGetters[key]
            // use computed to leverage its lazy-caching mechanism
            computed[key] = function () { return fn(store); }
            Object.defineProperty(store.getters, key, {
                get: function () { return store._vm[key]; }
            })
        })

        // use a Vue instance to store the state tree
        // suppress warnings just in case the user has added
        // some funky global mixins
        var silent = Vue.config.silent
        Vue.config.silent = true
        store._vm = new Vue({
            data: { state: state },
            computed: computed
        })
        Vue.config.silent = silent

        // enable strict mode for new vm
        if (store.strict) {
            enableStrictMode(store)
        }

        if (oldVm) {
            // dispatch changes in all subscribed watchers
            // to force getter re-evaluation.
            store._withCommit(function () {
                oldVm.state = null
            })
            Vue.nextTick(function () { return oldVm.$destroy(); })
        }
    }

    function installModule (store, rootState, path, module, hot) {
        var isRoot = !path.length
        var state = module.state;
        var actions = module.actions;
        var mutations = module.mutations;
        var getters = module.getters;
        var modules = module.modules;

        // set state
        if (!isRoot && !hot) {
            var parentState = getNestedState(rootState, path.slice(0, -1))
            var moduleName = path[path.length - 1]
            store._withCommit(function () {
                Vue.set(parentState, moduleName, state || {})
            })
        }

        if (mutations) {
            Object.keys(mutations).forEach(function (key) {
                registerMutation(store, key, mutations[key], path)
            })
        }

        if (actions) {
            Object.keys(actions).forEach(function (key) {
                registerAction(store, key, actions[key], path)
            })
        }

        if (getters) {
            wrapGetters(store, getters, path)
        }

        if (modules) {
            Object.keys(modules).forEach(function (key) {
                installModule(store, rootState, path.concat(key), modules[key], hot)
            })
        }
    }

    function registerMutation (store, type, handler, path) {
        if ( path === void 0 ) path = [];

        var entry = store._mutations[type] || (store._mutations[type] = [])
        entry.push(function wrappedMutationHandler (payload) {
            handler(getNestedState(store.state, path), payload)
        })
    }

    function registerAction (store, type, handler, path) {
        if ( path === void 0 ) path = [];

        var entry = store._actions[type] || (store._actions[type] = [])
        var dispatch = store.dispatch;
        var commit = store.commit;
        entry.push(function wrappedActionHandler (payload, cb) {
            var res = handler({
                dispatch: dispatch,
                commit: commit,
                getters: store.getters,
                state: getNestedState(store.state, path),
                rootState: store.state
            }, payload, cb)
            if (!isPromise(res)) {
                res = Promise.resolve(res)
            }
            if (store._devtoolHook) {
                return res.catch(function (err) {
                    store._devtoolHook.emit('vuex:error', err)
                    throw err
                })
            } else {
                return res
            }
        })
    }

    function wrapGetters (store, moduleGetters, modulePath) {
        Object.keys(moduleGetters).forEach(function (getterKey) {
            var rawGetter = moduleGetters[getterKey]
            if (store._wrappedGetters[getterKey]) {
                console.error(("[vuex] duplicate getter key: " + getterKey))
                return
            }
            store._wrappedGetters[getterKey] = function wrappedGetter (store) {
                return rawGetter(
                    getNestedState(store.state, modulePath), // local state
                    store.getters, // getters
                    store.state // root state
                )
            }
        })
    }

    function enableStrictMode (store) {
        store._vm.$watch('state', function () {
            assert(store._committing, "Do not mutate vuex store state outside mutation handlers.")
        }, { deep: true, sync: true })
    }

    function getNestedState (state, path) {
        return path.length
            ? path.reduce(function (state, key) { return state[key]; }, state)
            : state
    }

    function install (_Vue) {
        if (Vue) {
            console.error(
                '[vuex] already installed. Vue.use(Vuex) should be called only once.'
            )
            return
        }
        Vue = _Vue
        applyMixin(Vue)
    }

// auto install in dist mode
    if (typeof window !== 'undefined' && window.Vue) {
        install(window.Vue)
    }

    var index = {
        Store: Store,
        install: install,
        mapState: mapState,
        mapMutations: mapMutations,
        mapGetters: mapGetters,
        mapActions: mapActions
    }

    return index;

})));